﻿using System;
using System.Threading;
using System.Threading.Tasks;

namespace JESAI.DistributedLock.Internal;

/// <summary>
///一个基于异步的、<see cref="SyncViaAsync"/>友好的互斥体，基于<see cref="SemaphoreSlim"/>。我们不公开一个<see cref="IDisposable.Deplate"/>方法，因为<see cref="SemaphoreSlim"/>不需要处理，除非访问其<see cref="SemphaeroSlim.AvailableWaitHandle"/>
/// </summary>
internal readonly struct AsyncLock
{
    private readonly SemaphoreSlim _semaphore;

    private AsyncLock(SemaphoreSlim semaphore)
    {
        this._semaphore = semaphore;
    }

    public static AsyncLock Create() => new(new SemaphoreSlim(initialCount: 1, maxCount: 1));

    public async ValueTask<IDisposable> AcquireAsync(CancellationToken cancellationToken)
    {
        var handle = await this.TryAcquireAsync(Timeout.InfiniteTimeSpan, cancellationToken).ConfigureAwait(false);
        Invariant.Require(handle != null);
        return handle!;
    }

    public async ValueTask<IDisposable?> TryAcquireAsync(TimeoutValue timeout, CancellationToken cancellationToken)
    {
        var acquired = SyncViaAsync.IsSynchronous
            ? this._semaphore.Wait(timeout.InMilliseconds, cancellationToken)
            : await this._semaphore.WaitAsync(timeout.InMilliseconds, cancellationToken).ConfigureAwait(false);
        return acquired ? new Handle(this._semaphore) : null;
    }

    private sealed class Handle(SemaphoreSlim semaphore) : IDisposable
    {
        private SemaphoreSlim? _semaphore = semaphore;

        public void Dispose() => Interlocked.Exchange(ref this._semaphore, null)?.Release();
    }
}
